package com.special.threademo.common.jwt;


import com.special.threademo.common.excel.Base64Util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author LiuYC
 * @version 1.0.0
 * @create 2020-12-24 13:14
 */
@Component
@Slf4j
public class JwtTokenUtil {
    @Resource
    RedisTemplate<String, Object> redisTemplate;
    private static RedisTemplate<String, Object> REDIS_TEMPLATE;

    @PostConstruct
    public void init() {
        JwtTokenUtil.REDIS_TEMPLATE = this.redisTemplate;
    }

    // private static Logger log = LoggerFactory.getLogger(JwtTokenUtil.class);
    // jwt配置信息,秘钥
    public static final String base64Secret = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY=";
    private static final String CLIENT_ID = "098f6bcd4621d373cade4e832627b4f6";
    //设置1天过期
    private static final int EXPIRES_SECOND = 3600 * 24;

    /**
     * 因为在修改app登录逻辑,所以添加一个给后管登录用
     *
     * @param userId
     * @return
     */
    public static String adminCreateJwt(String userId) {
        //userId是重要信息，进行加密下
        String encryId = Base64Util.encode(userId);
        //先判断redis中是否存在，不存在再创建
        if (isTokenValid(encryId)) {
            ValueOperations<String, Object> vo = REDIS_TEMPLATE.opsForValue();
            return (String) vo.get(encryId);
        }
        try {
            SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
            byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(base64Secret);
            Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

            //添加构成JWT的参数
            JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT").claim("userId", encryId).setIssuer(CLIENT_ID).setIssuedAt(new Date()).signWith(signatureAlgorithm, signingKey);
            String token = builder.compact();
            REDIS_TEMPLATE.opsForValue().set(encryId, token, EXPIRES_SECOND, TimeUnit.SECONDS);
            return token;
        } catch (Exception e) {
            log.error("签名失败", e);
            throw new IndexOutOfBoundsException();
        }
    }

    /**
     * 解析jwt
     */
    public static Claims parseJwt(String jsonWebToken, String base64Security) {
        try {
            Claims claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(base64Security)).parseClaimsJws(jsonWebToken).getBody();
            return claims;
        } catch (Exception e) {
            log.error("===== token解析异常 =====", e);
            throw new IndexOutOfBoundsException();
        }
    }

    /**
     * 从redis中判断token是否过期
     *
     * @param userId 这个userId是加密以后的
     */
    public static boolean isTokenValid(String userId) {
        ValueOperations<String, Object> vo = REDIS_TEMPLATE.opsForValue();
        String o = (String) vo.get(userId);
        if (o != null) {
            return true;
        }
        return false;
    }

    /**
     * 验证token
     */
    public static Boolean verifyToken(String token) {
        try {
            Claims claims = parseJwt(token, base64Secret);
            if (claims == null) {
                return false;
            }
            String id = (String) claims.get("userId");

            if (!isTokenValid(id)) {
                return false;
            }
            String redisToken = (String) REDIS_TEMPLATE.opsForValue().get(id);
            return StringUtils.equals(redisToken, token);
        } catch (Exception e) {
            log.error("Token验证失败，{}", e.getMessage());
            return false;
        }
    }

    /**
     * 从token中获取用户ID
     */
    public static String getUserId(String token, String base64Security) {
        String userId = parseJwt(token, base64Security).get("userId", String.class);
        return Base64Util.decode(userId);
    }


}
